.. _Параметры const ref и константные функции-члены:

Параметры const ref и константные функции-члены
===============================================

.. index::
    const ref
    ref
    immutable

В этой главе рассказывается о том, как параметры и функции-члены помечаются как
const, чтобы их можно было использовать и с неизменяемыми переменными. Поскольку мы
уже рассматривали параметры const в предыдущих главах, некоторая информация в этой
главе будет обзором некоторых функций, которые вы уже знаете.
Хотя в примерах в этой главе используется только struct,
функции-члены const применимы и к классам.

Неизменяемые объекты
--------------------
Мы уже видели, что модифицировать неизменяемые переменные невозможно:

.. code-block:: d

    immutable readingTime = TimeOfDay(15, 0)

readingTime не может быть изменен:

.. code-block:: d

    readingTime = TimeOfDay(16, 0); // ← ошибка компиляции
    readingTime.minute += 10; // ← ошибка компиляции

Компилятор не позволяет каким-либо образом изменять неизменяемые объекты.

Параметры ref, которые не являются const
----------------------------------------

Мы уже встречались с этим понятием ранее в главе (:ref:`Параметры функций<глава_35_start>`).
Параметры, помеченные как ref, могут свободно изменяться функцией. По
поэтому, даже если функция фактически не изменяет параметр, компилятор
компилятор не позволяет передавать в качестве этого параметра неизменяемые объекты:

.. code-block:: d

    /* Although not being modified by the function, 'duration'
    * is not marked as 'const' */
    int totalSeconds(ref Duration duration)
    {
        return 60 * duration.minute;
    }
    // ...

    immutable warmUpTime = Duration(3);
    totalSeconds(warmUpTime); // ← ошибка компиляции

Компилятор не позволяет передавать неизменяемое значение warmUpTime в
totalSeconds, поскольку эта функция не гарантирует, что параметр
не будет изменён

Параметры const ref
-------------------

const ref означает, что параметр не изменяется функцией

.. code-block:: d

    int totalSeconds(const ref Duration duration)
    {
        return 60 * duration.minute;
    }
    // ...
    immutable warmUpTime = Duration(3);
    totalSeconds(warmUpTime); // ← now compiles

Такие функции могут принимать неизменяемые объекты в качестве параметров, потому что
неизменяемость объекта обеспечивается компилятором:

.. code-block:: d

    int totalSeconds(const ref Duration duration)
    {
        duration.minute = 7; // ← compilation ERROR
        // ...
    }

Альтернативой const ref является in ref. Как мы увидим в одной из следующих глав (:ref:`Параметры функций<глава_35_start>`),
in означает, что параметр используется только как вход в функцию, запрещая
любые его модификации.

.. code-block:: d

    int totalSeconds(in ref Duration duration)
    {
    // ...
    }

.. index::
    функции-члены без const


Функции-члены без const
-----------------------
Как мы уже видели на примере функции-члена TimeOfDay.increment, объекты можно изменять и с помощью функций-членов.
также могут быть изменены с помощью функций-членов. increment() изменяет
члены объекта, на котором она вызвана


.. code-block:: d

    struct TimeOfDay
    {
        // ...
        void decrement(Duration duration)
        {
            // ... please implement this function ...
        }

        unittest
        {
            auto time = TimeOfDay(10, 30);
            // A trivial test
            time.decrement(Duration(12));
            assert(time == TimeOfDay(10, 18));
            // 3 days and 11 hours earlier
            time.decrement(Duration(3 * 24 * 60 + 11 * 60));
            assert(time == TimeOfDay(23, 18));
            // 23 hours and 18 minutes earlier must be midnight
            time.decrement(Duration(23 * 60 + 18));
            assert(time == TimeOfDay(0, 0));
            // 1 minute earlier
            time.decrement(Duration(1));
            assert(time == TimeOfDay(23, 59));
        }
    }


Функции-члены const
-------------------

.. index::
    функции-члены const

Некоторые функции-члены не вносят никаких изменений в объект, который они
вызываются. Примером такой функции является toString():

.. code-block:: d

    struct TimeOfDay
    {
        // ...
        string toString()
        {
            return format("%02s:%02s", hour, minute);
        }
        // ...
    }

Поскольку вся цель функции toString() - представить объект в строковом формате
в любом случае, она не должна модифицировать объект.
Тот факт, что функция-член не изменяет объект, объявляется с помощью ключевого слова
ключевым словом const после списка параметров:

.. code-block:: d

    struct TimeOfDay
    {
        // ...
        string toString() const
        {
            return format("%02s:%02s", hour, minute);
        }
    }

Это значение const гарантирует, что сам объект не будет изменен
функцией-членом. Как следствие, функцию-член toString() разрешено
вызывать даже для неизменяемых объектов. В противном случае функция toString() структуры
не была бы вызвана

..  code-block:: d

    struct TimeOfDay
    {
        // ...
        // Inferior design: Not marked as 'const'
        string toString()
        {
            return format("%02s:%02s", hour, minute);
        }
    }
    // ...
    immutable start = TimeOfDay(5, 30);
    writeln(start); // TimeOfDay.toString() is not called!

Результатом является не ожидаемое значение 05:30, указывающее на то, что
вместо TimeOfDay.toString вызывается универсальная функция:

.. code-block:: console

    immutable(TimeOfDay)(5, 30)

Кроме того, явный вызов toString() на неизменяемом объекте приведет к ошибке
ошибку компиляции:

.. code-block:: d

    auto s = start.toString(); // ← ошибка компиляции


Соответственно, функции toString(), которые мы определили в предыдущей главе
все они были спроектированы неправильно; их следовало обозначить как
const.
*Примечание: Ключевое слово const может быть указано как перед определением функции, так и после*

.. index::
    функции-члены inout

Функции-члены inout
-------------------

Как мы уже видели в главе "Параметры функций" (стр. 168), inout передает
изменчивость параметра в возвращаемый тип.
Аналогично, функция-член inout передает изменяемость объекта в
возвращаемый тип функции:

.. code-block::

    import std.stdio;

    struct Container
    {
        int[] elements;
        inout(int)[] firstPart(size_t n) inout
        {
            return elements[0 .. n];
        }
    }

    void main()
    {
        {
            // An immutable container
            auto container = immutable(Container)([1, 2, 3]);
            auto slice = container.firstPart(2);
            writeln(typeof(slice).stringof);
        }

        {
            // A const container
            auto container = const(Container)([1, 2, 3]);
            auto slice = container.firstPart(2);
            writeln(typeof(slice).stringof);
        }
        {
            // A mutable container
            auto container = Container([1, 2, 3]);
            auto slice = container.firstPart(2);
            writeln(typeof(slice).stringof);
        }
    }



Три фрагмента, возвращаемые тремя объектами с различной мутабельностью, соответствуют
соответствуют объектам, которые их вернули

.. code-block:: d

    immutable(int)[]
    const(int)[]
    int[]

Поскольку она должна вызываться и на const, и на неизменяемых объектах, функция-член inout
компилируется так же, как если бы она была const.

Как использовать
----------------
* Чтобы гарантировать, что параметр не будет изменен функцией, пометьте
  этот параметр как in, const или const ref.
* Пометьте функции-члены, которые не изменяют объект, как const:

.. code-block:: d

    struct TimeOfDay
    {
        // ...
        string toString() const
        {
            return format("%02s:%02s", hour, minute);
        }
    }

Это сделает структуру (или класс) более полезной, устранив ненужное
ограничение. В остальных примерах книги мы будем придерживаться этого правила.


